package com.vhly.appspot.twitter;

import com.vhly.appspot.HttpParameter;
import com.vhly.appspot.util.StreamUtil;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * User: vhly
 * Date: 2009-11-3
 * Time: 10:58:38
 *
 * @author vhly[FR]
 */
public final class RequestData implements Serializable {

    private static final long serialVersionUID = 958809372235L;

    private static Logger log = Logger.getLogger("RequestData");

    /**
     * API Category
     */
    public byte category;
    /**
     * Method
     */
    public byte method;

    /**
     * API Method Code
     */
    public byte code;

    /**
     * Request Method Format
     */
    public byte format;
    /**
     * HTTP Method
     */
    public byte httpmethod;
    /**
     * Authorize Type
     */
    public byte authType;
    /**
     * 是否包括id参数
     */
    public byte hasIDSet;
    /**
     * 参数设置
     */
    public List<HttpParameter> params;

    public byte[] authData;

    private String identifier;

    private String consumerSecret = "nx0K0AyciIMYVAoLiA04kUzoVXjXwG3KUUpUfbNYeKI";
    private String consumerKey = "bOTAsQ6nHrBeyGdh3gTDA";
    private String callback;
    private String token;
    private String tokenSecret;
    private String verifier;

    public static final String CONSUMER_KEY = "ck";
    public static final String TOKEN = "tk";
    public static final String TOKEN_SECRET = "tks";
    public static final String CALLBACK = "cb";
    public static final String VERIFIER = "ve";
    private static final String CONSUMERSECRET = "cs";

    private List<HttpParameter> innerOAParams;

    public RequestData() {
        hasIDSet = 0;
        authType = Constants.AUTH_TYPE_NONE;
        httpmethod = Constants.HTTP_API_GET;
        format = Constants.FORMAT_TYPE_XML;
        params = new ArrayList<HttpParameter>();
        innerOAParams = new ArrayList<HttpParameter>();
        identifier = null;
    }

    /**
     * 读取参数
     *
     * @param in Data
     * @return boolean Success
     */
    public boolean read(InputStream in) {
        boolean bret = false;
        if (in != null) {
            DataInputStream din = null;
            try {
                int iv;
                din = new DataInputStream(in);
                iv = din.readByte();
                category = (byte) ((iv >> 4) & 0x0f);
                method = (byte) (iv & 0x0f);
                code = (byte) iv;
                iv = din.readByte();
                httpmethod = (byte) ((iv >> 4) & 0x0f);  //0
                authType = (byte) ((iv) & 0x0f);    //0
                iv = din.readByte();
                format = (byte) ((iv >> 4) & 0x0f);
                hasIDSet = (byte) (iv & 0x0f);
                // arguments count max 255
                iv = din.read();
                String v1, v2;
                for (int i = 0; i < iv; i++) {
                    v1 = din.readUTF();
                    v2 = din.readUTF();
                    params.add(new HttpParameter(v1, v2));
                }
                if (authType == Constants.AUTH_TYPE_BASIC) {
                    iv = din.readShort();
                    if (iv > 0) {
                        // TODO read auth data
                        authData = new byte[iv];
                        //noinspection ResultOfMethodCallIgnored
                        din.read(authData);
                    }
                } else if (authType == Constants.AUTH_TYPE_OAUTH) {
                    // consumerSecret set not need ,because to set default
//                    consumerSecret = din.readUTF();
                    int num = din.readShort();
                    String k, v;
                    for (int i = 0; i < num; i++) {
                        k = din.readUTF();
                        v = din.readUTF();
                        if (CONSUMER_KEY.equals(k)) {
                            this.consumerKey = v;
                        } else if (TOKEN.equals(k)) {
                            token = v;
                        } else if (TOKEN_SECRET.equals(k)) {
                            tokenSecret = v;
                        } else if (CALLBACK.equals(k)) {
                            callback = v;
                        } else if (VERIFIER.equals(k)) {
                            verifier = v;
                        } else if (CONSUMERSECRET.equals(k)) {
                            consumerSecret = v;
                        }
//                        oparams.add(new HttpParameter(k,v));
                    }
                }
                bret = true;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                StreamUtil.close(din);
            }
        }
        return bret;
    }

    public boolean read(byte[] data) {
        boolean bret = false;
        if (data != null && data.length > 0) {
            try {
                log.info("Read buf len is " + data.length);
                int iv = data[0];
                category = (byte) ((iv >> 4) & 0x0f);
                method = (byte) (iv & 0x0f);
                code = (byte) iv;
                iv = data[1];
                httpmethod = (byte) ((iv >> 4) & 0x0f);  //0
                authType = (byte) ((iv) & 0x0f);    //0
                iv = data[2];
                format = (byte) ((iv >> 4) & 0x0f);
                hasIDSet = (byte) (iv & 0x0f);
                iv = data[3]; // params count
                String k, v;
                int h, l;
                int at = 4;
                int count = iv;
                for (int i = 0; i < count; i++) {
                    h = data[at];
                    l = data[at + 1];
                    at += 2;
                    iv = ((h & 0x0ff) << 8) | (l & 0x0ff);
                    k = new String(data, at, iv, "UTF-8");
                    at += iv;
                    h = data[at];
                    l = data[at + 1];
                    at += 2;
                    iv = ((h & 0x0ff) << 8) | (l & 0x0ff);
                    v = new String(data, at, iv, "UTF-8");
                    at += iv;
                    params.add(new HttpParameter(k, v));
                }
                if (authType == Constants.AUTH_TYPE_BASIC) { // Basic, OAuth
                    log.info("Read Basic");
                    h = data[at];
                    l = data[at + 1];
                    at += 2;
                    iv = ((h & 0x0ff) << 8) | (l & 0x0ff);
                    if (iv > 0) {
                        // TODO read auth data
                        authData = new byte[iv];
                        System.arraycopy(data, at, authData, 0, iv);
                    }
                } else if (authType == Constants.AUTH_TYPE_OAUTH) { // Basic, OAuth
                    log.info("read oauth info");
                    h = data[at];
                    l = data[at + 1];
                    at += 2;
                    int oapslen = ((h & 0x0ff) << 8) | (l & 0x0ff);
                    log.info("oa params size is " + iv);
                    for (int i = 0; i < oapslen; i++) {
                        h = data[at];
                        l = data[at + 1];
                        at += 2;
                        iv = ((h & 0x0ff) << 8) | (l & 0x0ff);
                        k = new String(data, at, iv, "UTF-8");
                        at += iv;
                        h = data[at];
                        l = data[at + 1];
                        at += 2;
                        iv = ((h & 0x0ff) << 8) | (l & 0x0ff);
                        v = new String(data, at, iv, "UTF-8");
                        at += iv;
                        System.out.println("oaparam " + k + " = " + v);
                        if (CONSUMER_KEY.equals(k)) {
                            consumerKey = v;
                        } else if (TOKEN.equals(k)) {
                            token = v;
                        } else if (TOKEN_SECRET.equals(k)) {
                            tokenSecret = v;
                        } else if (CALLBACK.equals(k)) {
                            callback = v;
                        } else if (VERIFIER.equals(k)) {
                            verifier = v;
                        } else if (CONSUMERSECRET.equals(k)) {
                            consumerSecret = v;
                        }
                        innerOAParams.add(new HttpParameter(k, v));
                    }
                }
                bret = true;
            } catch (Exception e) {
                log.warning("Exception: " + e.getMessage());
                throw new RuntimeException(e);
            }
        }
        return bret;
    }

    public String getToken() {
        return token;
    }

    public void seToken(String token) {
        this.token = token;
        // debug
        innerOAParams.add(new HttpParameter(TOKEN, token));
    }

    public String getTokenSecret() {
        return tokenSecret;
    }

    public void setTokenSecret(String accessTokenSecret) {
        this.tokenSecret = accessTokenSecret;
        innerOAParams.add(new HttpParameter(TOKEN_SECRET, accessTokenSecret));
    }

    public String getCallback() {
        return callback;
    }

    public void setCallback(String callback) {
        this.callback = callback;
        innerOAParams.add(new HttpParameter(CALLBACK, callback));
    }

    public String getConsumerKey() {
        return consumerKey;
    }

    public void setConsumerKey(String consumerKey) {
        this.consumerKey = consumerKey;
        innerOAParams.add(new HttpParameter(CONSUMER_KEY, consumerKey));
    }

    public String getVerifier() {
        return verifier;
    }

    public void setVerifier(String verifier) {
        this.verifier = verifier;
        innerOAParams.add(new HttpParameter(VERIFIER, verifier));
    }

    public String getConsumerSecret() {
        return consumerSecret;
    }

    public void setConsumerSecret(String consumerSecret) {
        this.consumerSecret = consumerSecret;
    }

    public void write(OutputStream out) {
        if (out != null) {
            DataOutputStream dout = null;
            try {
                dout = new DataOutputStream(out);
                code = (byte) (((category & 0x0f) << 4) | (method & 0x0f));
                dout.writeByte(code);
//                int ic = ((httpmethod & 0x03) << 6) | ((authType & 0x0f) << 4) | ((format & 0x03) << 2) | (hasIDSet & 0x03);
                int ic = ((httpmethod & 0x0f) << 4) | ((authType & 0x0f));
                dout.writeByte(ic);
                ic = ((format & 0x0f) << 4) | (hasIDSet & 0x0f);
                dout.writeByte(ic);
                ic = params.size();
                dout.write(ic);
                if (ic > 0) {
                    String k, v;
                    for (int i = 0; i < ic; i++) {
                        HttpParameter hp = params.get(i);
                        k = hp.name;
                        v = hp.value;
                        dout.writeUTF(k);
                        dout.writeUTF(v);
                    }
                }
                if (authType == Constants.AUTH_TYPE_BASIC) {
                    if (authData != null) {
                        ic = authData.length;
                        dout.writeShort(ic);
                        if (ic > 0) {
                            dout.write(authData);
                        }
                    } else {
                        dout.writeShort(0);
                    }
                } else if (authType == Constants.AUTH_TYPE_OAUTH) {
//                    dout.writeUTF(consumerSecret);
                    int len = innerOAParams.size();
                    dout.writeShort(len);
                    if (len > 0) {
                        for (int i = 0; i < len; i++) {
                            HttpParameter hp = innerOAParams.get(i);
                            dout.writeUTF(hp.name);
                            dout.writeUTF(hp.value);
                        }
                    }
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                StreamUtil.close(dout);
            }
        }
    }

    public String getIdentifier() {
        String ret = null;
        if (identifier == null) {
            StringBuffer sb = new StringBuffer();
            sb.append(category).append(':').append(method).append(':').append(format).append(':')
                    .append(httpmethod).append(':').append(authType).append(':').append(hasIDSet);
            ret = sb.toString();
            identifier = ret;
        }
        return ret;
    }

    @Override
    public boolean equals(Object obj) {
        boolean bret = false;
        if (obj instanceof RequestData) {
            RequestData rd = (RequestData) obj;
            bret = rd.category == category && rd.method == method && rd.format == format && rd.hasIDSet == hasIDSet
                    && rd.httpmethod == httpmethod;
            if (bret) {
                bret = rd.authType == authType;
                if (bret) {
                    // TODO 比较授权部分
                    if (authData != null && rd.authData != null) {
                        bret = Arrays.equals(rd.authData, authData);
                    }
                }
                if (bret) {
                    bret = rd.params.equals(params);
                }
            }
        }
        return bret;
    }

    public void dealloc() {
        params.clear();
        params = null;
    }
}
